// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: declaration of CImplModel.
//   $Id$


// Model.h: interface for the CImplModel class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MODEL_H__D09A37C8_C99B_45B1_8931_8F88EDDA5249__INCLUDED_)
#define AFX_MODEL_H__D09A37C8_C99B_45B1_8931_8F88EDDA5249__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "DOM3Core.h"
#include <DOM3XPath.h>
#include "FormControl.h"
#include "vertex.h"
#include "IUIEvents.h"
#include <list>
#include <map>
class CXFormsParseModel;
class CXFormsElementModel;
class CImplSubmitInfo;
class CImplInstance;
typedef  std::list<const CImplSubmitInfo * > CSubmissionList;
#include "mapBSTRToInstancePtr.h"
typedef std::map<CComBSTR,CComQIPtr<IDispatch> > CMapCComBSTRToIDispatchPtr; 
typedef std::pair<CComBSTR,CComQIPtr<IDispatch> > CPairBSTRIDispatchPtr;
typedef std::list<CComBSTR > CComBSTRList;
class CImplModel : public CImpl
{
	friend class CXFormsParseModel;
public:
	virtual ~CImplModel();
	CImplModel(CXFormsElementModel * pXForms);

	virtual HRESULT init(CImpl * pParent);

	inline void AddFormControl(CFormControl* pFormControl);

	HRESULT getInstanceNumber(BSTR sXPath, double* pdData,IXMLDOMNode * pContext = 0);

	bool getInstanceBoolean(IXMLDOMNode * pContext, BSTR sRef);
	HRESULT getInstanceData(BSTR sXPath, CNode* pContextNode, IXPathResult**ppData);
	HRESULT getInstanceString(BSTR sXPath, CNode* pContextNode, BSTR* psData);
	HRESULT getInstanceNode(BSTR sXPath, CNode* pContextNode,bool bCreateIfNotFound, CNode** ppNode);
	HRESULT getInstanceNodeList(BSTR sXPath, CNode* pContextNode, bool bCreateIfNotFound, IXPathResult** ppResult);

	HRESULT putInstanceString(BSTR sXPath, CNode* pContextNode, BSTR sData);
	HRESULT putInstanceData(CNode* pRecipientNode,  IXPathResult * ppData);

	//XForms events
	HRESULT recalculate(bool bDefer);
	HRESULT doRecalculate();
	HRESULT refresh(bool bDefer);
	HRESULT doRefresh();
	HRESULT rebuild(bool bDefer);
	HRESULT doRebuild();

	virtual HRESULT initialize();
	HRESULT initializeSilently();

	HRESULT reset();
	HRESULT UIInitialize();
	HRESULT revalidate(bool bDefer);
	HRESULT validate();
	HRESULT doRevalidate();
	void createPertinentDependencySubgraph(CTypedPtrList<CPtrList,CVertex*> * arLc,/*out*/CTypedPtrList<CPtrList,CVertex*> * arSubgraph);
	
	CComBSTR m_sSchemaURLs;

	CMapCComBSTRToIDispatchPtr m_Libraries;
	HRESULT GetEvaluator(IXPathEvaluator ** ppEvaluator);
		
	void AddAsyncSubmission(const CImplSubmitInfo* const pSubmission);
	void RemoveAsyncSubmission(const CImplSubmitInfo* const pSubmission);
	HRESULT GetDefaultEvaluationContext(IXMLDOMNode ** ppNode);
	void deleteControl(CFormControl * pControl);
	virtual void notifyDelete();
	CVertexList * m_pMasterDependencyList;
	CVertexList * m_pPertinentSubGraph;
	void onInstanceReset(CImplInstance * pInst, CDOM3Document * pDoc);
	//finds which instance a node comes from
	HRESULT findInstance(IXMLDOMNode * pNode, CImplInstance ** ppInstance);
	CImplInstance * getInstance(BSTR sInstanceId = NULL);
	void notifyDestroy();
	//error events:
	//void onSubmitError(BSTR const sURI,BSTR const sReason);
	void onLinkError(BSTR const sURI);
	void onLinkException(BSTR const sURI);
	//specified behaviour is that this event does not have any information
	//	attached, but for this function to display a message, I think it should.
	void onBindingException(BSTR const sStatement,const BSTR sReason= L"");


	void AddBind(CImplBind * pBind);
	HRESULT RemoveFormControl(CFormControl * pFC);
 	
 
	CComBSTR m_sExtensionFunctions;

	HRESULT addVertex
	(
		IXMLDOMNode * pContext,
		CComBSTR sRef,
		bool bApplyFirstNodeRule,
		CVertex::EType eType,
		CVertex * vDependent,
		CTypedPtrArray<CPtrArray,CVertex*>  * pListToFill
		,CIdResolver * pResolver
	);
	CComPtr<IXMLDOMSchemaCollection> m_pSchemaCache; 
	HRESULT initializeNewControls(void);
	// Called to add a vertex from the masterDependecy tree to the pertinent subgraph
	HRESULT makePertinent(CVertex * pVertex);
	// Initialises any controls bound to this model that have not yet been initialised.  Returns true if any initialisations occured during this call.
	bool initializePendingControls(void);
	// principally called by CImplActionSetValue
	HRESULT setValue(BSTR const sBind , BSTR const sRef , BSTR const sValueRef , BSTR const sLiteral , IXMLDOMNode * pEvaluationContext);
	void onComputeException(const char * const sReason);
	inline CMapBSTRToInstancePtr* getInstanceList(void);
	// <summary>Fires the ready event using both the event notification systems</summary>
	HRESULT ready(void);
	inline bool isInitalised();
	// <summary>fetches a numeric value for at in insert and delete</summary>
	HRESULT getNodesetActionLocation(IXMLDOMNode * pContext,BSTR const bsAt,const unsigned long max, unsigned long * pulRetval);
	HRESULT ConfigureForm(void) const;
	void CImplModel::AddDocumentToLookupTable(CImplInstance *pInst, CDOM3Document *pDoc);
	void CImplModel::RemoveDocumentFromLookupTable(CDOM3Document *pDoc);

	// <summary>removes xsi:type attributes from anywhere in pNode</summary>
	//<remarks>ALWAYS use paired with replaceXSITypeAttributes, as it leaves placeholders</remarks>
	HRESULT removeXSITypeAttributes(IXMLDOMNode * pNode) const;
	// <summary>replaces xsi:type attributes removed by removeXSITypeAttributes</summary>
	HRESULT replaceXSITypeAttributes(IXMLDOMNode * pNode) const;

private:
	bool m_bSilentRunning;
	bool m_bDoSilentUpdate;
	CSubmissionList m_AsyncSubmissions;
	HRESULT loadFunctions();
	HRESULT loadSchemas();
	HRESULT loadInstances();
	HRESULT loadSchema(BSTR sURL);
	void createMasterDependencyList();
	//storage of binds, one to match id to a bind, the other to store them all.
	//the map c(sh)ould be put into the top level element.
//	CMapStringToPtr m_BindIds;
	CTypedPtrList<CPtrList,CImplBind*> m_Binds;

	//XForms events
	HRESULT construct(void);

	HRESULT refreshNodeBindings();
	bool m_bRefreshPending;
	bool m_bRebuildPending;
	bool m_bRecalculatePending;
	bool m_bRevalidatePending;

	bool m_bCreateDataFromFormControls;
	//Should be stored as CImplNodeBinding*, Apart from deletion on shutdown, there is no place
	//	where a pointer is fetched from this list and NOT cast to CImplNodeBinding*.
	CTypedPtrList<CPtrList, CFormControl*> m_FormControls;
	CTypedPtrList<CPtrList, CFormControl*> m_DeadFormControls;
	
	//list to contain controls that need to be initialized
	//	on next refresh, even if the model does not need to be refreshed.
	CTypedPtrList<CPtrList, CFormControl*> m_pendingControls;


	HRESULT addInstance(BSTR id, CImplInstance * pInstance);

	HRESULT burnDeadControls();
	CMapPtrToPtr m_UnkInstances;
	CMapBSTRToInstancePtr m_Instances;
	CImplInstance* m_pInstance;

	//pointer to an object used to notify
	//	the UI and host application of any events.
///	IUIModelEvents * m_pHostEventHandler;

//	CTypedPtrList<CPtrList,IXMLDOMNode *> m_RecentInsertions;

	// Indicates that the Model has been Initalised. The inital value is false, 
	// set to true at the UIInitalise() which internal is called on DocumentReady().
	bool m_bInitalised;
	bool m_bCreatingMDL;
	CComBSTR m_bsConfig;  // Stores the URL from the @fp:config attribute, if any [bug 267].
	CComBSTRList m_lstAddedSchemata;

};

inline bool CImplModel::isInitalised()
{
	return m_bInitalised;
}


inline void CImplModel::AddFormControl(CFormControl* pFormControl)
{
	//Save wasteful processing at runtime by only including
	//	each control once.
	if(pFormControl && !m_FormControls.Find(pFormControl,NULL))
	{
		m_FormControls.AddTail(pFormControl);
		//If a new control has been added, 
		//	a refresh is required.
//		m_bRefreshPending = true;
		//If this is happening after the document is complete,
		//	ie. content added "live" by an output or DOM access
		//	to the HTML, initialize the control.
		if(m_pXForms->isDocumentComplete())
		{
			m_pendingControls.AddTail(pFormControl);
		}

	}
}

inline CMapBSTRToInstancePtr* CImplModel::getInstanceList()
{
	return &m_Instances;
}

#endif // !defined(AFX_MODEL_H__D09A37C8_C99B_45B1_8931_8F88EDDA5249__INCLUDED_)
